/**
 * useSyncExternalStore() - 订阅外部数据的变化
 *    概念：
 *      允许我们订阅外部数据源，当外部数据发生变化时，可以获取到的。
 *      一般也是哪些类库开发人员去使用的
 *
 *    语法：
 *        const state = useSyncExternalStore(subscribe, getSnapshot)
 *          1. subscribe - 订阅函数，内部需要返回取消订阅的函数
 *                         初始化阶段，会触发 subscribe 函数一次，且会携带一个 listener（订阅者） 过去。
 *                         在该函数中，需要将 listener 做保存，以供后续通知他们我的数据有变化，
 *          2. getSnapshot - 获取快照的一个函数，该函数内部需要返回一份数据，返回的数据就是 state
 *                         当外部源通知订阅者有数据变化之后，订阅者会重新调用 getSnapshot 从而得到外部数据源中最新的数据
 */

import { nanoid } from "nanoid";
import { useSyncExternalStore } from "react";

// 实现一个 todoStore （待办事项的仓库 - 外部数据源）

let listeners = []; // 用于存放订阅者函数

const todoStore = {
  // 待办事项集合数据
  todos: [{ id: nanoid(), title: "吃饭" }],

  // 增加 todo 项
  addTodo: () => {
    const length = todoStore.todos.length;
    // 不要修改源数据
    // todoStore.todos.push({
    //   id: nanoid(),
    //   title: `Todo #${length}`,
    // });
    todoStore.todos = [
      ...todoStore.todos,
      {
        id: nanoid(),
        title: `Todo #${length}`,
      },
    ];

    // 数据源变化了，需要通知订阅者
    console.log(listeners, "listeners");
    // listeners[0](); // 不能只通知一个人，应该全部通知到位
    listeners.forEach((fn) => {
      fn();
    });
  },

  // subscribe 方法，供 useSyncExternalStore 使用
  //    listener 是 组件的订阅者，需要将它们存起来。供后续通知他们我数据有更新
  //             如何通知他们更新，就将他们传递过来的额 listener 给调用一下即可。
  subscribe: (listener) => {
    console.log("subscribe");
    listeners.push(listener); // 存储起来

    // 返回一个函数，取消订阅，该函数会在组件销毁时触发
    return () => {
      // 做一些取消订阅的事情
      //      如何取消订阅？？，将当前 订阅者函数 从 listeners 中 移除掉即可
      listeners = listeners.filter((l) => l !== listener);
    };
  },

  // getSnapshot 方法，供 useSyncExternalStore 使用
  getSnapshot: () => {
    console.log("getSnapshot");

    // 返回一份数据出去，该数据得是一份全新的数据，不能是一个引用对象的形式，如果是引用数据，组件将无法更新
    return todoStore.todos;
  },
};

function Hello() {
  // 使用外部仓库
  const todos = useSyncExternalStore(
    todoStore.subscribe,
    todoStore.getSnapshot
  );
  return (
    <div>
      <h4>Hello</h4>
      <ul>
        {todos.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
}

function App() {
  const todos = useSyncExternalStore(
    todoStore.subscribe,
    todoStore.getSnapshot
  );

  console.log("App todos", todos);

  return (
    <div>
      <h1>App</h1>
      <button onClick={todoStore.addTodo}>AddTodo</button>
      <hr />
      <ul>
        {todos.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
      <hr />
      <Hello />
    </div>
  );
}

export default App;
